home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2000 October
/
CHIP Turkiye Ekim 2000.iso
/
prog
/
naps
/
04
/
setup.exe
/
Gnucleus
/
ListCtrlEx.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-07-11
|
30KB
|
1,170 lines
/********************************************************************************
Gnucleus - A node application for the Gnutella network
Copyright (C) 2000 John Marshall
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For support, questions, comments, etc...
E-Mail:
swabby@c0re.net
Address:
21 Cadogan Way
Nashua, NH, USA 03062
********************************************************************************/
#include "stdafx.h"
#include <assert.h>
#include "ListCtrlEx.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Used for inplace edits on the control
#include "InPlaceEdit.h"
/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx
IMPLEMENT_DYNCREATE(CListCtrlEx, CListCtrl)
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
//{{AFX_MSG_MAP(CListCtrlEx)
ON_WM_PAINT()
ON_WM_SETFOCUS()
ON_WM_KILLFOCUS()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONDOWN()
ON_WM_KEYDOWN()
ON_WM_DESTROY()
ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndlabeledit)
//}}AFX_MSG_MAP
ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)
ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
ON_MESSAGE(LVM_SETTEXTCOLOR, OnSetTextColor)
ON_MESSAGE(LVM_SETTEXTBKCOLOR, OnSetTextBkColor)
ON_MESSAGE(LVM_SETBKCOLOR, OnSetBkColor)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx construction/destruction
CListCtrlEx::CListCtrlEx()
{
// Don't Highlight entire row by default.
m_bFullRowSel = FALSE;
// Allow selections to be made all the way across the rows
m_bClientWidthSel = TRUE;
// Default to not showing the grid lines.
m_bDrawGrid = FALSE;
// Set default sorting behaviour
nSortedCol = -1;
bSortAscending = TRUE;
// Allow the columns to be re-arranged
m_headerctrl.SetCallback( this, (void (CWnd::*)(int, int))DragColumn );
m_clrText = ::GetSysColor(COLOR_WINDOWTEXT);
m_clrTextBk = ::GetSysColor(COLOR_WINDOW);
m_clrBkgnd = ::GetSysColor(COLOR_WINDOW);
// Use generic sort routine by default, disable and enable
// with EnableGenericSort().
m_bUseGenericSort = TRUE;
}
CListCtrlEx::~CListCtrlEx()
{
}
/**
* Find the number of columns in the control
*/
int CListCtrlEx::GetNColumns()
{
return( m_headerctrl.GetItemCount() );
}
CString CListCtrlEx::GetColumnTitle( int n )
{
// Get the column text and format
TCHAR buf[256];
HD_ITEM hditem;
hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = buf;
hditem.cchTextMax = 255;
m_headerctrl.GetItem( n , &hditem );
return( CString ( buf ) );
}
/**
* Return the column number associated with a title.
* This is necessary because the normal Windows method for adding
* text to a row depends upon knowing the column number for which
* you wish to insert text. But we hijack the column numbers by
* allowing users to rearrange them...
*/
int CListCtrlEx::GetColumnNumber( char *title )
{
int nColumns = m_headerctrl.GetItemCount();
for ( int i = 0; i < nColumns; i++ )
{
HD_ITEM data;
char name[200];
data.mask = HDI_TEXT;
data.pszText = (LPSTR)name;
data.cchTextMax = sizeof( name );
m_headerctrl.GetItem( i, &data );
if ( strcmp( title, data.pszText) == 0 )
{
return( i );
}
}
return( -1 );
}
/**
* Used for the header column dragging behaviour
*/
void CListCtrlEx::DragColumn(int source, int dest)
{
TCHAR sColText[160];
// Insert a column at dest
LV_COLUMN lv_col;
lv_col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv_col.pszText = sColText;
lv_col.cchTextMax = 159;
GetColumn( source, &lv_col );
lv_col.iSubItem = dest;
InsertColumn( dest, &lv_col );
// Adjust source col number since it might have changed
// because a new column was inserted
if( source > dest )
source++;
// Moving a col to position 0 is a special case
if( dest == 0 )
for( int i = GetItemCount()-1; i > -1 ; i-- )
SetItemText(i, 1, GetItemText( i, 0) );
// Copy sub item from source to dest
for( int i = GetItemCount()-1; i > -1 ; i-- )
SetItemText(i, dest, GetItemText( i, source ) );
// Delete the source column, but not if it is the first
if( source != 0 )
DeleteColumn( source );
else
{
// If source col is 0, then copy col# 1 to col#0
// and then delete col# 1
GetColumn( 1, &lv_col );
lv_col.iSubItem = 0;
SetColumn( 0, &lv_col );
for( int i = GetItemCount()-1; i > -1 ; i-- )
SetItemText(i, 0, GetItemText( i, 1) );
DeleteColumn( 1 );
}
Invalidate();
}
/**
* Make sure the control is owner drawn, this means that we can
* provide the custom drawing behaviour of; full row selection, and
* grid lines
*/
BOOL CListCtrlEx::PreCreateWindow(CREATESTRUCT& cs)
{
// default is report view and full row selection
cs.style &= ~LVS_TYPEMASK;
cs.style |= LVS_REPORT | LVS_OWNERDRAWFIXED | LVS_EDITLABELS ;
m_bFullRowSel = TRUE;
return(CListCtrl::PreCreateWindow(cs));
}
/**
* Hook into the message loop to show the tooltips.
*/
BOOL CListCtrlEx::PreTranslateMessage(MSG* pMsg)
{
m_tooltip.RelayEvent( pMsg );
return CListCtrl::PreTranslateMessage(pMsg);
}
/**
* Allow the whole row to be highlighted in the list, not just the first
* column.
*/
BOOL CListCtrlEx::SetFullRowSel(BOOL bFullRowSel)
{
// no painting during change
LockWindowUpdate();
m_bFullRowSel = bFullRowSel;
BOOL bRet;
if (m_bFullRowSel)
bRet = ModifyStyle(0L, LVS_OWNERDRAWFIXED);
else
bRet = ModifyStyle(LVS_OWNERDRAWFIXED, 0L);
// repaint window if we are not changing view type
if (bRet && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
Invalidate();
// repaint changes
UnlockWindowUpdate();
return(bRet);
}
/**
* Determine whether or not full row selection is enabled.
*/
BOOL CListCtrlEx::GetFullRowSel()
{
return(m_bFullRowSel);
}
/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx drawing
/*
* DrawItem() is called by the framework whenever an item needs to be drawn
* for owner drawn controls.
* Note:
* <UL>
* <LI>LVS_SHOWSELALWAYS: non owner drawn controls show an item is
* highlighted when the control does not have focus with a different
* highlight color is (usually gray). This is not supported for
* this control.
* </UL>
*/
void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
int iSavedDC = pDC->SaveDC(); // Save DC state
int iItem = lpDrawItemStruct->itemID;
// Get item image and state info
LV_ITEM lvi;
lvi.mask = LVIF_IMAGE | LVIF_STATE;
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.stateMask = 0xFFFF; // get all state flags
GetItem(&lvi);
bool bHighlight = (
(lvi.state & LVIS_DROPHILITED) ||
((lvi.state & LVIS_SELECTED) && ((GetFocus() == this) || (GetStyle() & LVS_SHOWSELALWAYS)))
);
// Get rectangles for drawing
CRect rcBounds;
CRect rcLabel;
CRect rcIcon;
GetItemRect(iItem, rcBounds, LVIR_BOUNDS);
GetItemRect(iItem, rcLabel, LVIR_LABEL);
GetItemRect(iItem, rcIcon, LVIR_ICON);
CRect rcItem(rcBounds);
CString sLabel = GetItemText(iItem, 0);
// Labels are offset by a certain amount
// This offset is related to the width of a space character
int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
rcBounds.left = rcLabel.left;
CRect rcWnd;
GetClientRect(&rcWnd);
if(m_bClientWidthSel && rcBounds.right<rcWnd.right)
rcBounds.right = rcWnd.right;
// Draw the background
if(bHighlight)
{
pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
pDC->FillRect(rcBounds, &CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
}
else
{
pDC->FillRect(rcBounds, &CBrush(m_clrTextBk));
}
// Set clip region
rcItem.right = rcItem.left + GetColumnWidth(0);
// Draw state icon
if(lvi.state & LVIS_STATEIMAGEMASK)
{
int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
CImageList* pImageList = GetImageList(LVSIL_STATE);
if(pImageList)
{
pImageList->Draw(pDC, nImage,
CPoint(rcItem.left, rcItem.top), ILD_TRANSPARENT);
}
}
// Draw normal and overlay icon
CImageList* pImageList = GetImageList(LVSIL_SMALL);
if(pImageList)
{
UINT nOvlImageMask = lvi.state & LVIS_OVERLAYMASK;
pImageList->Draw(pDC, lvi.iImage,
CPoint(rcIcon.left, rcIcon.top),
(bHighlight?ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );
}
// Draw item label - Column 0
rcLabel.left += offset/2-1;
rcLabel.right -= offset;
pDC->DrawText(sLabel,-1,rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX
| DT_VCENTER | DT_END_ELLIPSIS);
// Draw labels for remaining columns
LV_COLUMN lvc;
lvc.mask = LVCF_FMT | LVCF_WIDTH;
for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
{
rcItem.left = rcItem.right;
rcItem.right += lvc.cx;
sLabel = GetItemText(iItem, nColumn);
// Get the text justification
UINT nJustify = DT_LEFT;
switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
{
case LVCFMT_RIGHT:
nJustify = DT_RIGHT;
break;
case LVCFMT_CENTER:
nJustify = DT_CENTER;
break;
default:
break;
}
rcLabel = rcItem;
rcLabel.left += offset;
rcLabel.right -= offset;
pDC->DrawText(sLabel, -1, rcLabel,
nJustify | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);
}
// draw focus rectangle if item has focus
if ((lvi.state & LVIS_FOCUSED) && (GetFocus() == this))
pDC->DrawFocusRect(rcBounds);
pDC->RestoreDC(iSavedDC); // Restore DC.
}
/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx diagnostics
#ifdef _DEBUG
void CListCtrlEx::Dump(CDumpContext& dc) const
{
CListCtrl::Dump(dc);
dc << "m_bFullRowSel = " << m_bFullRowSel;
dc << "\n";
}
#endif //_DEBUG
/**
* @param iRow [in] row of cell
* @param iColunm [in] column of cell
* @return Rectangle corresponding to the given cell.
*/
CRect CListCtrlEx::GetCellRect(int iRow, int iColumn)const
{
// Make sure that the ListView is in LVS_REPORT
if((GetStyle() & LVS_TYPEMASK) != LVS_REPORT)
return CRect(0,0,0,0);
// Get the number of columns
{
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int iColumnCount = pHeader->GetItemCount();
ASSERT(iColumn < iColumnCount);
}
CRect rect;
GetItemRect(iRow, &rect, LVIR_BOUNDS);
// Now find the column
for(int colnum = 0; colnum < iColumn; colnum++)
{
rect.left += GetTrueColumnWidth(colnum);
}
// Found the column
rect.right = rect.left + GetTrueColumnWidth(iColumn);
RECT rectClient;
GetClientRect(&rectClient);
if(rect.right > rectClient.right)
rect.right = rectClient.right;
return rect;
}
/**
* @author Mark Findlay
*/
CString CListCtrlEx::GetTrueItemText(int row, int col)const
{
// Get the header control
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
_ASSERTE(pHeader);
// get the current number of columns
int nCount = pHeader->GetItemCount();
// find the actual column requested. We will compare
// against hi.iOrder
for (int x=0; x< nCount; x++)
{
HD_ITEM hi = {0};
hi.mask = HDI_ORDER;
BOOL bRet = pHeader->GetItem(x,&hi);
_ASSERTE(bRet);
if (hi.iOrder == col)
{
// Found it, get the associated text
return GetItemText(row,x);
}
}
_ASSERTE(FALSE);
return "We better never fall through to here!";
}
/**
* @author Mark Findlay
*/
int CListCtrlEx::GetTrueColumnWidth(int nCurrentPosition)const
{
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
_ASSERTE(pHeader);
int nCount = pHeader->GetItemCount();
for (int x=0; x< nCount; x++)
{
HD_ITEM hi = {0};
hi.mask = HDI_WIDTH | HDI_ORDER;
BOOL bRet = pHeader->GetItem(x,&hi);
_ASSERTE(bRet);
if (hi.iOrder == nCurrentPosition)
return hi.cxy;
}
_ASSERTE(FALSE);
return 0; // We would never fall through to here!
}
void CListCtrlEx::HideTitleTip()
{
m_titletip.ShowWindow(SW_HIDE);
}
/**
* @param point [in] point in client coordinates
* @param iRow [out] row containing the point
* @param iColunm [out] column containing the point
*
* @author Matthew Bells
*/
bool CListCtrlEx::HitTestRowCol(CPoint& point, int& iRow, int& iColumn)
{
// Make sure that the ListView is in LVS_REPORT
if((GetStyle() & LVS_TYPEMASK) != LVS_REPORT)
return false;
int iPosX = point.x;
if(m_bFullRowSel)
iRow = HitTest(point);
// the above doesn't work when the control isn't owner drawn
// this will work on machines with Version 4.70 and later of Comctl32.dll.
else
{
LVHITTESTINFO hit_test_info;
hit_test_info.pt = point;
SubItemHitTest(&hit_test_info);
iRow = hit_test_info.iItem;
}
// Get the number of columns
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int iColumnCount = pHeader->GetItemCount();
for(iColumn = 0; iColumn < iColumnCount; ++iColumn)
{
iPosX -= GetTrueColumnWidth(iColumn);
if(iPosX < 0)
break;
}
if(iColumn == iColumnCount)
iColumn = -1;
return (iRow != -1 && iColumn != -1);
}
// HitTestEx - Determine the row index and column index for a point
// Returns - the row index or -1 if point is not over a row
// point - point to be tested.
// col - to hold the column index
int CListCtrlEx::HitTestEx(CPoint &point, int *col) const
{
int colnum = 0;
int row = HitTest( point, NULL );
if( col ) *col = 0;
// Make sure that the ListView is in LVS_REPORT
if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
return row;
// Get the top and bottom row visible
row = GetTopIndex();
int bottom = row + GetCountPerPage();
if( bottom > GetItemCount() )
bottom = GetItemCount();
// Get the number of columns
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// Loop through the visible rows
for( ;row <=bottom;row++)
{
// Get bounding rect of item and check whether point falls in it.
CRect rect;
GetItemRect( row, &rect, LVIR_BOUNDS );
if( rect.PtInRect(point) )
{
// Now find the column
for( colnum = 0; colnum < nColumnCount; colnum++ )
{
int colwidth = GetColumnWidth(colnum);
if( point.x >= rect.left
&& point.x <= (rect.left + colwidth ) )
{
if( col ) *col = colnum;
return row;
}
rect.left += colwidth;
}
}
}
return -1;
}
// EditSubLabel - Start edit of a sub item label
// Returns - Temporary pointer to the new edit control
// nItem - The row index of the item to edit
// nCol - The column of the sub item.
CEdit* CListCtrlEx::EditSubLabel( int nItem, int nCol )
{
// The returned pointer should not be saved
// Make sure that the item is visible
if( !EnsureVisible( nItem, TRUE ) ) return NULL;
// Make sure that nCol is valid
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
return NULL;
// Get the column offset
int offset = 0;
for( int i = 0; i < nCol; i++ )
offset += GetColumnWidth( i );
CRect rect;
GetItemRect( nItem, &rect, LVIR_BOUNDS );
// Now scroll if we need to expose the column
CRect rcClient;
GetClientRect( &rcClient );
if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
{
CSize size;
size.cx = offset + rect.left;
size.cy = 0;
Scroll( size );
rect.left -= size.cx;
}
// Get Column alignment
LV_COLUMN lvcol;
lvcol.mask = LVCF_FMT;
GetColumn( nCol, &lvcol );
DWORD dwStyle ;
if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
dwStyle = ES_LEFT;
else if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
dwStyle = ES_RIGHT;
else dwStyle = ES_CENTER;
rect.left += offset+4;
rect.right = rect.left + GetColumnWidth( nCol ) - 3 ;
if( rect.right > rcClient.right) rect.right = rcClient.right;
dwStyle |= WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
CEdit *pEdit = new CInPlaceEdit(nItem, nCol, GetItemText( nItem, nCol ));
#define IDC_IPEDIT 2
pEdit->Create( dwStyle, rect, this, IDC_IPEDIT );
return pEdit;
}
/**
* Repaint the selected items.
*/
void CListCtrlEx::RepaintSelectedItems()
{
CRect rcItem;
CRect rcLabel;
// Invalidate focused item so it can repaint
int iItem = GetNextItem(-1, LVNI_FOCUSED);
if(iItem != -1)
{
GetItemRect(iItem, rcItem, LVIR_BOUNDS);
GetItemRect(iItem, rcLabel, LVIR_LABEL);
rcItem.left = rcLabel.left;
InvalidateRect(rcItem, FALSE);
}
// Invalidate selected items depending on LVS_SHOWSELALWAYS
if(!(GetStyle() & LVS_SHOWSELALWAYS))
{
for(iItem = GetNextItem(-1, LVNI_SELECTED);
iItem != -1; iItem = GetNextItem(iItem, LVNI_SELECTED))
{
GetItemRect(iItem, rcItem, LVIR_BOUNDS);
GetItemRect(iItem, rcLabel, LVIR_LABEL);
rcItem.left = rcLabel.left;
InvalidateRect(rcItem, FALSE);
}
}
UpdateWindow();
}
/////////////////////////////////////////////////////////////////////////////
// CListCtrlEx message handlers
void CListCtrlEx::OnDestroy()
{
m_titletip.DestroyWindow();
CListCtrl::OnDestroy();
}
void CListCtrlEx::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CListCtrl::OnChar(nChar, nRepCnt, nFlags);
HideTitleTip();
SendSelChangedNotification();
}
void CListCtrlEx::OnKillFocus(CWnd* pNewWnd)
{
CListCtrl::OnKillFocus(pNewWnd);
// This should be hidden no matter if another control is getting focus
// or the edit box.
HideTitleTip();
// this really still has focus if one of its chilren (ie. the edit box)
// has focus
if(pNewWnd != NULL && pNewWnd->GetParent() == this)
return;
// repaint items that should change appearance
if(m_bFullRowSel && (GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
RepaintSelectedItems();
}
void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
{
int iTest = GetKeyState(VK_LMENU);
// Shortcut to editing.
if((GetKeyState(VK_LMENU) & 0x8000) || (GetKeyState(VK_RMENU) & 0x8000))
{
int iRow;
int iColumn;
if(HitTestRowCol(point, iRow, iColumn))
{
SetFocus();
PostMessage(LVM_EDITLABEL, (WPARAM)iRow, 0);
}
}
else
{
CListCtrl::OnLButtonDown(nFlags, point);
ShowTitleTip(point); // Make sure TitleTip changes if needed.
SendSelChangedNotification();
}
}
void CListCtrlEx::OnMouseMove(UINT nFlags, CPoint point)
{
if( nFlags == 0 )
{
ShowTitleTip(point); // Make sure TitleTip changes if needed.
}
CListCtrl::OnMouseMove(nFlags, point);
}
/*
* When the regular list view control repaints an item, it repaints only the
* area occupied by defined columns. If the last column does not extend to the
* end of the client area, then the space to the right of the last column is
* not repainted. If we are highlighting the full row then this area also needs
* to be invalidated so that the code in DrawItem() can add or remove the
* highlighting from this area.
*/
void CListCtrlEx::OnPaint()
{
// in full row select mode, we need to extend the clipping region
// so we can paint a selection all the way to the right
if (m_bClientWidthSel &&
(GetStyle() & LVS_TYPEMASK) == LVS_REPORT && GetFullRowSel())
{
CRect rcAllLabels;
GetItemRect(0, rcAllLabels, LVIR_BOUNDS);
CRect rcClient;
GetClientRect(&rcClient);
if(rcAllLabels.right < rcClient.right)
{
// need to call BeginPaint (in CPaintDC c-tor)
// to get correct clipping rect
CPaintDC dc(this);
CRect rcClip;
dc.GetClipBox(rcClip);
rcClip.left = min(rcAllLabels.right-1, rcClip.left);
rcClip.right = rcClient.right;
InvalidateRect(rcClip, FALSE);
// EndPaint will be called in CPaintDC d-tor
}
}
CListCtrl::OnPaint();
// Draw the lines only for LVS_REPORT mode
if ( ( (GetStyle() & LVS_TYPEMASK) == LVS_REPORT ) &&
( m_bDrawGrid ) )
{
// Get the number of columns
CClientDC dc(this );
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// The bottom of the header corresponds to the top of the line
RECT rect;
pHeader->GetClientRect( &rect );
int top = rect.bottom;
// Now get the client rect so we know the line length and
// when to stop
GetClientRect( &rect );
// The border of the column is offset by the horz scroll
int borderx = 0 - GetScrollPos( SB_HORZ );
for( int i = 0; i < nColumnCount; i++ )
{
// Get the next border
borderx += GetColumnWidth( i );
// if next border is outside client area, break out
if( borderx >= rect.right ) break;
// Draw the line.
dc.MoveTo( borderx-1, top);
dc.LineTo( borderx-1, rect.bottom );
}
// Draw the horizontal grid lines
// First get the height
if( !GetItemRect( 0, &rect, LVIR_BOUNDS ))
return;
int height = rect.bottom - rect.top;
GetClientRect( &rect );
int width = rect.right;
for( i = 1; i <= GetCountPerPage(); i++ )
{
dc.MoveTo( 0, top + height*i);
dc.LineTo( width, top + height*i );
}
}
// Do not call CListCtrl::OnPaint() for painting messages
}
/**
* See if we should draw the grid.
*/
BOOL CListCtrlEx::GetDrawGrid()
{
return( m_bDrawGrid );
}
/**
* Set whether we should draw the grid or not.
*/
void CListCtrlEx::SetDrawGrid(BOOL bDrawGrid )
{
m_bDrawGrid = bDrawGrid;
// Force a redraw
Invalidate();
}
LRESULT CListCtrlEx::OnSetBkColor(WPARAM wParam, LPARAM lParam)
{
m_clrBkgnd = (COLORREF)lParam;
return(Default());
}
/*
* This is another step to mimic the default behaviour of the list view
* control. When the control loses focus, the focus rectangle around the
* selected (focus) item has to be removed. When the control gets back
* focus, then the focus rectangle has to be redrawn. Both these handlers
* call the RepaintSelectedItems() helper function.
*/
void CListCtrlEx::OnSetFocus(CWnd* pOldWnd)
{
CListCtrl::OnSetFocus(pOldWnd);
// check if we are getting focus from label edit box
// if(pOldWnd!=NULL && pOldWnd->GetParent()==this)
// return;
// repaint items that should change appearance
if(m_bFullRowSel && (GetStyle() & LVS_TYPEMASK)==LVS_REPORT)
RepaintSelectedItems();
}
LRESULT CListCtrlEx::OnSetTextBkColor(WPARAM wParam, LPARAM lParam)
{
m_clrTextBk = (COLORREF)lParam;
return(Default());
}
LRESULT CListCtrlEx::OnSetTextColor(WPARAM wParam, LPARAM lParam)
{
m_clrText = (COLORREF)lParam;
return(Default());
}
void CListCtrlEx::PreSubclassWindow()
{
CListCtrl::PreSubclassWindow();
// Add initialization code for our header
m_headerctrl.SubclassWindow( ::GetDlgItem(m_hWnd,0) );
// Adding a tooltip to the header control.
m_tooltip.Create( this );
m_tooltip.AddTool( GetDlgItem(0), "Click to sort on this column. Drag to rearrange columns." );
m_titletip.Create(this);
m_titletip.SetBackground(CBrush(GetBkColor()));
}
void CListCtrlEx::SendSelChangedNotification()
{
NMHDR nmh;
nmh.hwndFrom = *this;
nmh.idFrom = GetDlgCtrlID();
nmh.code = LVNU_SELCHANGED;
GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmh);
}
void CListCtrlEx::ShowTitleTip(CPoint point)
{
int iRow;
int iCol;
CRect rcIcon;
if(HitTestRowCol(point, iRow, iCol))
{
CRect cellrect = GetCellRect(iRow, iCol);
// offset is equal to TextExtent of 2 space characters.
// Make sure you have the right font selected into the
// device context before calling GetTextExtent.
// You can save this value as a member variable.
// offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;
int offset = 6;
/*if(iCol == 0)
{
CRect rcLabel;
GetItemRect(iRow, &rcLabel, LVIR_LABEL );
offset = rcLabel.left - cellrect.left + offset / 2 - 1;
}*/
if(iCol == 0) // TBD: test this with IE4
cellrect.left -= 2; // Does it also move the first column???
cellrect.top++;
if(GetItemState(iRow, LVIS_SELECTED)
&& (m_bFullRowSel || iCol == 0) )
{
m_titletip.SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
m_titletip.SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else
{
m_titletip.SetBkColor(m_clrTextBk);
m_titletip.SetTextColor(m_clrText);
}
// move over allowing for icon to show
if ( iCol == 0 )
{
GetItemRect(iRow, rcIcon, LVIR_ICON);
cellrect.left += rcIcon.Width();
cellrect.right += rcIcon.Width();
}
// make sure our calculated result is where the mouse is.
if( (point.x >= cellrect.left) && (point.x <= cellrect.right)
&& (point.y <= cellrect.bottom) && (point.y > cellrect.top) )
m_titletip.Show(cellrect, GetTrueItemText(iRow, iCol), offset-1);
}
}
void CListCtrlEx::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult)
{
HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
if( m_bUseGenericSort && phdn->iButton == 0 )
{ // User clicked on header using left mouse button
if( phdn->iItem == nSortedCol )
bSortAscending = !bSortAscending;
else
bSortAscending = TRUE;
nSortedCol = phdn->iItem;
SortTextItems( nSortedCol, bSortAscending );
}
*pResult = 0;
}
// SortTextItems - Sort the list based on column text
// Returns - Returns true for success
// nCol - column that contains the text to be sorted
// bAscending - indicate sort order
// low - row to start scanning from - default row is 0
// high - row to end scan. -1 indicates last row
BOOL CListCtrlEx::SortTextItems( int nCol, BOOL bAscending,
int low, int high)
{
if( nCol >= ((CHeaderCtrl*)GetDlgItem(0))->GetItemCount() )
return FALSE;
((CMyHeaderCtrl*)GetDlgItem( 0 ) )->RemoveAllSortImages();
((CMyHeaderCtrl*)GetDlgItem( 0 ) )->SetSortImage( nCol, bAscending );
// SetItemSortState( iSubItem , (SORT_STATE)!ssEachItem );
if( high == -1 ) high = GetItemCount() - 1;
int lo = low;
int hi = high;
CString midItem;
if( hi <= lo ) return FALSE;
midItem = GetItemText( (lo+hi)/2, nCol );
// loop through the list until indices cross
while( lo <= hi )
{
// rowText will hold all column text for one row
CStringArray rowText;
// find the first element that is greater than or equal to
// the partition element starting from the left Index.
if( bAscending )
while( ( lo < high ) && ( GetItemText(lo, nCol) < midItem ) )
++lo;
else
while( ( lo < high ) && ( GetItemText(lo, nCol) > midItem ) )
++lo;
// find an element that is smaller than or equal to
// the partition element starting from the right Index.
if( bAscending )
while( ( hi > low ) && ( GetItemText(hi, nCol) > midItem ) )
--hi;
else
while( ( hi > low ) && ( GetItemText(hi, nCol) < midItem ) )
--hi;
// if the indexes have not crossed, swap
// and if the items are not equal
if( lo <= hi )
{
// swap only if the items are not equal
if( GetItemText(lo, nCol) != GetItemText(hi, nCol))
{
// swap the rows
LV_ITEM lvitemlo, lvitemhi;
int nColCount =
((CHeaderCtrl*)GetDlgItem(0))->GetItemCount();
rowText.SetSize( nColCount );
int i;
for( i=0; i<nColCount; i++)
rowText[i] = GetItemText(lo, i);
lvitemlo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
lvitemlo.iItem = lo;
lvitemlo.iSubItem = 0;
lvitemlo.stateMask = LVIS_CUT | LVIS_DROPHILITED |
LVIS_FOCUSED | LVIS_SELECTED |
LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;
lvitemhi = lvitemlo;
lvitemhi.iItem = hi;
GetItem( &lvitemlo );
GetItem( &lvitemhi );
for( i=0; i<nColCount; i++)
SetItemText(lo, i, GetItemText(hi, i));
lvitemhi.iItem = lo;
SetItem( &lvitemhi );
for( i=0; i<nColCount; i++)
SetItemText(hi, i, rowText[i]);
lvitemlo.iItem = hi;
SetItem( &lvitemlo );
}
++lo;
--hi;
}
}
// If the right index has not reached the left side of array
// must now sort the left partition.
if( low < hi )
SortTextItems( nCol, bAscending , low, hi);
// If the left index has not reached the right side of array
// must now sort the right partition.
if( lo < high )
SortTextItems( nCol, bAscending , lo, high );
return TRUE;
}
/**
* User has finished editting a label.
*/
void CListCtrlEx::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM *plvItem = &pDispInfo->item;
if (plvItem->pszText != NULL)
{
SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
}
*pResult = 0;
}
BOOL CListCtrlEx::SetItemText( int nItem, int nSubItem, LPCTSTR lpszText )
{
if ( nSubItem < 0 )
return FALSE;
else
return( CListCtrl::SetItemText( nItem, nSubItem, lpszText ) );
}
void CListCtrlEx::EnableGenericSort(BOOL _use_generic)
{
m_bUseGenericSort = _use_generic;
}